const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const Analyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const DashboardPlugin = require('webpack-dashboard/plugin');
const path = require('path');
const webpack = require('webpack');
const HappyPack = require('happypack');
const ENV = process.env.ENV;
const isProd = ENV === 'production';
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common.config.js');
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();
const SizePlugin = require('size-plugin');

const webpackConfig = merge(commonConfig, {
  // 入口文件配置
  // entry: './src/js/app.js',
  // entry: {
  //   foo: './src/js/foo.js',
  // },
  entry: {
    foo: './src/js/foo.js',
    // bar: './src/js/bar.js',
    // 抽离第三方库到单独的文件中
    // vendor: ['react'],
  },
  // entry: {
  //   app: './src/js/app.js',
  //   vendor: ['react'],
  // },
  output: {
    // 初始化加载的文件的输出名
    filename: isProd ? './js/[name]@[chunkhash].js' : './js/[name].js',
    // 按需加载的文件的输出名
    chunkFilename: isProd ? './js/[name]@[chunkhash].js' : './js/[name].js',
    // 打包输出目录路径
    path: path.resolve(__dirname, 'dist'),
    // 浏览器访问输出目录的 URL
    // publicPath: '/dist/',
  },
  mode: ENV,
  // 开发环境打包速度和源码信息还原程度的一个折中选择
  devtool: 'eval-cheap-module-source-map',
  // 开发服务器
  devServer: {
    // 在服务器启动后打开浏览器
    open: true,
    // 开启 HMR，还需要配置插件
    hot: true,
    // 静态资源文件夹配置，单个为对象，多个为对象数组
    // static: {
    // 提供静态资源的本地文件夹路径
    //   directory: path.join(__dirname, 'dist'),
    // 浏览器访问静态资源文件夹的 URL
    //   publicPath: '/dist/',
    // },
  },
  cache: {
    type: 'filesystem',
    // 缓存目录名，默认为 ${config.name}-${config.mode}
    name: `nodejs-${process.version}`,
    cacheDirectory: path.resolve(__dirname, '.temp_cache'),
    // 当检测到cache.buildDependencies中的文件或目录发生改动时，Webpack会令现有的缓存失效
    buildDependencies: {
      myWebpackConfig: [__filename],
    },
  },
  plugins: [
    // 每次打包自动生成 index.html 文件并引用入口文件相关资源
    new HtmlWebpackPlugin({
      // 引用不加 defer
      scriptLoading: 'blocking',
      // title 标签内容
      title: path.basename(__dirname),
      // 模板 html 文件
      template: './src/html/template.html',
    }),
    // 抽离 css 到单独的文件中，并添加 link 标签来引用该文件
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
    }),
    // 每次打包自动清理打包目录
    new CleanWebpackPlugin(),
    // js 代码中可以使用定义的变量(编译时替换)
    new webpack.DefinePlugin({
      ENV: JSON.stringify(ENV),
    }),
    // 使模块id会根据自身内容进行哈希，然后生成一个与之相关的数字id
    new webpack.ids.HashedModuleIdsPlugin(),
    // 生成打包结构图
    // new Analyzer(),
    // 打包加速
    new HappyPack({
      id: 'js',
      loaders: [
        {
          loader: 'babel-loader',
          options: {
            presets: [['@babel/preset-react', { modules: false }]],
            cacheDirectory: true,
          },
        },
      ],
    }),
    // 会报错，无法解决
    // new HappyPack({
    //   id: 'ts',
    //   loaders: [
    //     {
    //       loader: 'ts-loader',
    //       options: {},
    //     },
    //   ],
    // }),
    // 排除 moment.js 的语言包模块
    new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/, // 匹配资源文件
      contextRegExp: /moment$/, // 匹配检索目录
    }),
    // 链接到 dll/vendor.js
    new webpack.DllReferencePlugin({
      manifest: require(path.join(__dirname, 'dll/react.manifest.json')),
    }),
    // lodash 会报错
    // new webpack.DllReferencePlugin({
    //   manifest: require(path.join(__dirname, 'dll/lodash.manifest.json')),
    // }),
    // moment 会报错
    // new webpack.DllReferencePlugin({
    //   manifest: require(path.join(__dirname, 'dll/moment.manifest.json')),
    // }),
    // 控制台更直观地输出打包信息
    // new DashboardPlugin(),
    // 监控资源体积的变化（还不支持 webpack5）
    // new SizePlugin(),
    // 开启 HMR 的插件
    new webpack.HotModuleReplacementPlugin(),
  ],
  // 打包体积、性能优化
  optimization: {
    // 使用默认配置的 TerserPlugin 来最小化打包体积
    // minimize: true,
    // 使用一个或多个自定义的 TerserPlugin 来覆盖默认的最小化器
    // minimizer: [
    //   new TerserPlugin({
    //     parallel: true,
    //     terserOptions: {
    // https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
    //     },
    //   }),
    // ],
    // 保留默认的最小化器
    minimizer: [new CssMinimizerPlugin(), '...'],
    // 分包
    splitChunks: {
      // 对同步块（初始化加载）和异步块（按需加载）都进行优化
      chunks: 'all',
      // 分组来进行优化，配置同 splitChunks，且覆盖 splitChunks 配置
      // cacheGroups: {
      //   // 缓存组名
      //   commons: {
      //     // 块名
      //     name: 'commons',
      //     // 只对同步块进行优化
      //     chunks: 'initial',
      //     // 被多个块共享的最小次数，小于该次数就不会被抽离出来
      //     minChunks: 2,
      //   },
      // },
      // cacheGroups: {
      //   commons: {
      //     test: /[\\/]node_modules[\\/]/,
      //     // cacheGroupKey here is `commons` as the key of the cacheGroup
      //     name(module, chunks, cacheGroupKey) {
      //       const moduleFileName = module
      //         .identifier()
      //         .split('/')
      //         .reduceRight((item) => item);
      //       const allChunksNames = chunks.map((item) => item.name).join('~');
      //       return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
      //     },
      //     name: 'vendors',
      //     chunks: 'all',
      //   },
      // },
    },
  },
  module: {
    noParse: /\.dll\.js$/,
    rules: [
      // 支持 .js 文件引入 .css 文件
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: [
          // 抽离 css
          MiniCssExtractPlugin.loader,
          // 处理 css 的 import() 和 url()
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[name]__[local]__[hash:base64:5]',
              },
              sourceMap: true,
            },
          },
          // 根据 postcss.config.js 对 css 进行处理
          'postcss-loader',
        ],
      },
      // 打包加速
      {
        test: /\.js$/,
        exclude: [/node_modules/, /static\/dist/],
        include: /src\/scripts/,
        use: 'happypack/loader?id=js',
      },
      // 会报错，无法解决
      // {
      //   test: /\.ts$/,
      //   use: 'happypack/loader?id=ts',
      // },
      // js 转 ES5
      // {
      //   test: /\.js$/,
      //   exclude: /node_modules/,
      //   use: {
      //     loader: 'babel-loader',
      //     options: {
      //       cacheDirectory: true,
      //       presets: [
      //         // 一定要加上 modules: false，否则 Webpack 接收到的就都是转化过的 CommonJS 形式的模块，无法对死代码进行去除
      //         ['@babel/preset-env', { modules: false }],
      //       ],
      //     },
      //   },
      // },
      // 支持 .js 文件引入 .ts 文件
      {
        test: /\.ts$/,
        use: 'ts-loader',
      },
      // 支持 .js 文件引入 .html 文件
      {
        test: /\.html$/,
        use: 'html-loader',
      },
      // 支持 .js 文件引入图片文件
      {
        test: /\.(png|jpg|gif)$/,
        // type: 'asset/resource',
        // use: {
        //   loader: 'file-loader',
        //   options: {
        //     name: '[name].[ext]',
        //     publicPath: '/another-path/',
        //   },
        // },
        use: {
          // 小于阈值的图片会被自动转为 base64
          loader: 'url-loader',
          options: {
            limit: 10240,
            name: '[name].[ext]',
          },
        },
      },
      // 自定义 loader (添加严格模式)
      // {
      //   test: /\.js$/,
      //   use: {
      //     loader: 'force-strict-loader',
      //     options: {
      //       sourceMap: true,
      //     },
      //   },
      // },
      // 支持 .js 文件引入 .scss 文件
      {
        test: /\.scss$/,
        use: [
          // 把 css 放到生成的 style 标签中
          'style-loader',
          { loader: 'css-loader', options: { sourceMap: true } },
          { loader: 'sass-loader', options: { sourceMap: true } },
        ],
      },
      // 支持 .js 文件引入 .less 文件
      {
        test: /\.less$/,
        use: [
          // 把 css 放到生成的 style 标签中
          'style-loader',
          { loader: 'css-loader', options: { sourceMap: true } },
          { loader: 'less-loader', options: { sourceMap: true } },
        ],
      },
    ],
  },
});

// 会报错，无法解决
// module.exports = smp.wrap(webpackConfig);

module.exports = webpackConfig;
